#ifndef QTROWTABLEMODEL_H
#define QTROWTABLEMODEL_H

#include <QAbstractTableModel>
#include <QList>
#include <QSharedPointer>

template<typename T>
class QtRowTableModel : public QAbstractTableModel
{
	typedef T Element;
	typedef QSharedPointer<T> ElementPtr;

public:
	QtRowTableModel(bool readOnly = false, QObject *parent = nullptr)
		:QAbstractTableModel(parent)
		, mReadOnly(readOnly)
	{
	
	}

    int rowCount(const QModelIndex &parent = QModelIndex()) const override
	{ 
		return mRows.size(); 
	}

    int columnCount(const QModelIndex &parent = QModelIndex()) const override
	{ 
		return mHeaders.size(); 
	}

    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
	{
		if (orientation == Qt::Horizontal){
			return mHeaders.at(section);
		}
		else{
			return section;
		}
	}

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
	{
		if (!index.isValid())
			return QVariant();

		int row = index.row();
		int col = index.column();
		if (role == Qt::DisplayRole || role == Qt::EditRole){
			if (row >= 0 && row < mRows.size()){
                if(col == 0) {
                    return QString::number(row+1);
                }

				ElementPtr element = mRows.at(index.row());
				if (!element.isNull())
					return element->data(col);
			}
		}
		else if (role == Qt::TextAlignmentRole){
			return Qt::AlignCenter;
		}
		
		return QVariant();
	}

    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
	{
		if (mReadOnly || !index.isValid())
			return false;

		int row = index.row();
		int col = index.column();
		if (role == Qt::EditRole){
			if (row >= 0 && row < mRows.size()){
				ElementPtr element = mRows.at(index.row());
				if (!element.isNull()){
					return element->setData(col, value);
				}
			}
		}

		return QAbstractTableModel::setData(index, value, role);
	}

    Qt::ItemFlags flags(const QModelIndex &index) const override
	{
        Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
        return mReadOnly ? flags : (flags | Qt::ItemIsEditable);
	}

    void setHeaderLabels(const QStringList& headers){ mHeaders = headers; }

    ElementPtr get(int index) {
        return mRows.at(index);
    }

    void add(ElementPtr p){
        this->beginInsertRows(QModelIndex(),mRows.size(),mRows.size());
        mRows.append(p);
        this->endInsertRows();
    }

    void insert(ElementPtr p,int index) {
        this->beginInsertRows(QModelIndex(),p,p);
        mRows.insert(index,p);
        this->endInsertRows();
    }

    void remove(ElementPtr p) {
        int index = indexOf(p);
        if(index != -1){
            remove(index);
        }
    }

    void remove(int index) {
        if(index <0 ){
            return;
        }

        this->beginRemoveRows(QModelIndex(),index,index);
        mRows.removeAt(index);
        this->endRemoveRows();
    }

    int indexOf(ElementPtr p) const {
        return mRows.indexOf(p);
    }

    void clear() {
        this->beginResetModel();
        mRows.clear();
        this->endResetModel();
    }

    void move(int index,int step) {
        if(step==0){
            return;
        }else if(step>0){
            this->beginMoveRows(QModelIndex(),index,index,QModelIndex(),index+step+1);
        }else{
            this->beginMoveRows(QModelIndex(),index,index,QModelIndex(),index+step);
        }

        mRows.move(index,index+step);
        this->endMoveRows();
    }

private:
	QList<ElementPtr> mRows;
	QStringList mHeaders;
	bool mReadOnly = false;
};

#endif // QTROWTABLEMODEL_H
